package cn.catsdad.base.repository;

import cn.catsdad.base.entity.BaseEntity;
import cn.catsdad.constant.constant.ICommonWarningType;
import cn.catsdad.util.StringUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * base层repository实现
 *
 * @author xuyl
 * @version V1.0.0 2025/4/9
 * @since V100R001
 */
@SuppressWarnings({ICommonWarningType.UNCHECKED, ICommonWarningType.UNUSED})
public class BaseRepositoryImpl<T extends BaseEntity, ID> extends SimpleJpaRepository<T, ID> implements IBaseRepository<T, ID> {

    @Value("${app.core.db.batchSize}")
    private int BATCH_SIZE;
    protected final EntityManager entityManager;
    protected final JpaEntityInformation<T, ID> jpaEntityInformation;

    public BaseRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
        this.jpaEntityInformation = (JpaEntityInformation<T, ID>) entityInformation;
    }

    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
        this.jpaEntityInformation = (JpaEntityInformation<T, ID>) JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager);
    }

    @Override
    @Transactional
    public void batchSave(Collection<T> entities) {
        if (StringUtil.isNotEmpty(entities)) {
            List<T> entityList = new ArrayList<>(entities);
            for (int i = 0; i < entities.size(); i++) {
                T entity = entityList.get(i);
                if (jpaEntityInformation.isNew(entity)) {
                    entityManager.persist(entity);
                } else {
                    entityManager.merge(entity);
                }
                if (i % BATCH_SIZE == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            entityManager.flush();
            entityManager.clear();
        }
    }

    @Override
    @Transactional
    public void batchInsert(Collection<T> entities) {
        if (StringUtil.isNotEmpty(entities)) {
            List<T> entityList = new ArrayList<>(entities);
            for (int i = 0; i < entities.size(); i++) {
                T entity = entityList.get(i);
                entityManager.persist(entity);
                if (i % BATCH_SIZE == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            entityManager.flush();
            entityManager.clear();
        }
    }

    @Override
    @Transactional
    public void batchUpdate(Collection<T> entities) {
        if (StringUtil.isNotEmpty(entities)) {
            List<T> entityList = new ArrayList<>(entities);
            for (int i = 0; i < entities.size(); i++) {
                T entity = entityList.get(i);
                entityManager.merge(entity);
                if (i % BATCH_SIZE == 0) {
                    entityManager.flush();
                    entityManager.clear();
                }
            }
            entityManager.flush();
            entityManager.clear();
        }
    }

    @Override
    public List<T> findAll() {
        List<T> result = super.findAll();
        if (StringUtil.isEmpty(result)) {
            return new ArrayList<>();
        }
        return result.stream().filter(t -> !t.getIsDeleted()).collect(Collectors.toList());
    }

    @Override
    public List<T> findAllById(Iterable<ID> ids) {
        List<T> result = new ArrayList<>();
        if (StringUtil.isEmpty(ids)) {
            return result;
        }
        result = super.findAllById(ids);
        return result.stream().filter(t -> !t.getIsDeleted()).collect(Collectors.toList());
    }

    @Override
    public T findByIdIncludeDeleted(ID id) {
        return findById(id).orElse(null);
    }

    @Override
    public List<T> findByIdsIncludeDeleted(Iterable<ID> ids) {
        return super.findAllById(ids);
    }

    @Override
    @Transactional
    public void logicDelete(Iterable<ID> ids) {
        if (StringUtil.isNotEmpty(ids)) {
            List<T> exists = findAllById(ids);
            for (T t : exists) {
                t.setIsDeleted(true);
            }
            batchUpdate(exists);
        }
    }

    @Override
    @Transactional
    public void logicDelete(ID id) {
        logicDelete(Collections.singleton(id));
    }

    @Override
    @Transactional
    public void physicalDelete(Iterable<ID> ids) {
        if (StringUtil.isNotEmpty(ids)) {
            deleteAllById(ids);
        }
    }

    @Override
    @Transactional
    public void physicalDelete(ID id) {
        deleteById(id);
    }

    @Override
    public Class<T> getEntityClass() {
        return jpaEntityInformation.getJavaType();
    }

    @Override
    public String getQueryAlias() {
        return "t";
    }
}
